/*
 * Copyright (c) zhg2yqq Corp.
 * All Rights Reserved.
 */
package com.ming.common.dynamic.code.core;

import com.ming.common.dynamic.code.IStringCompiler;
import com.ming.common.dynamic.code.config.BaseProperties;
import com.ming.common.dynamic.code.dto.CompileResult;
import com.ming.common.dynamic.code.dto.StringJavaFileObject;
import com.ming.common.dynamic.code.exception.CompileException;
import com.ming.common.dynamic.code.factory.AbstractCompilerFactory;

import javax.tools.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;


/**
 * 源码编译
 * 
 * @version zhg2yqq v1.0
 * @author 周海刚, 2022年7月8日
 */
public class StringJavaCompiler extends AbstractStringCompiler implements IStringCompiler {
    public StringJavaCompiler() {
        super();
    }

    public StringJavaCompiler(AbstractCompilerFactory factory) {
        super(factory);
    }

    /**
     * 编译字符串源代码,编译失败在 diagnosticsCollector 中获取提示信息
     *
     * @param fullClassName class全名
     * @param sourceCode 源码字符串内容
     * @param properties 配置
     * @return CompileResult 编译后的结果
     * @throws CompileException
     */
    @Override
    public CompileResult compile(String fullClassName, String sourceCode, BaseProperties properties)
        throws CompileException {
        Map<String, String> sources = new HashMap<String, String>(1);
        sources.put(fullClassName, sourceCode);
        Map<String, CompileResult> results = this.compile(sources, properties);
        return results.get(fullClassName);
    }

    @Override
    public Map<String, CompileResult> compile(Map<String, String> sources,
                                              BaseProperties properties)
        throws CompileException {
        long compilerTakeTime = -1;
        boolean compileSuccess;
        // 存放编译过程中输出的信息
        DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>();
        Map<CompileResult, String> sourceResults = new HashMap<>(sources.size());
        for (Entry<String, String> en : sources.entrySet()) {
            sourceResults.put(new CompileResult(en.getKey()), en.getValue());
        }
        if (properties.isCalCompileTime()) {
            long startTime = System.currentTimeMillis();
            // 编译成字节，并将字节对象放入result
            compileSuccess = compile(sourceResults, diagnosticsCollector);
            // 设置编译耗时(单位ms)
            compilerTakeTime = System.currentTimeMillis() - startTime;
        } else {
            // 编译成字节，并将字节对象放入result
            compileSuccess = compile(sourceResults, diagnosticsCollector);
        }
        
        if (compileSuccess) {
            final long _compilerTakeTime = compilerTakeTime;
            return sourceResults.entrySet().stream().map(en -> {
                CompileResult r = en.getKey();
                r.setCompileTime(_compilerTakeTime);
                return r;
            }).collect(Collectors.toMap(CompileResult::getFullClassName, r -> r));
        }
        throw new CompileException(diagnosticsCollector);
    }

    /**
     * 核心编译
     * 
     * @param sourceCodes 源码
     * @param 编译错误信息
     * @return 是否编译成功
     */
    protected boolean compile(Map<CompileResult, String> sourceCodes,
                              DiagnosticCollector<JavaFileObject> diagnosticsCollector) {
        // 标准的内容管理器,更换成自己的实现，覆盖部分方法
        StandardJavaFileManager standardFileManager = getCompiler()
                .getStandardFileManager(diagnosticsCollector, null, null);
        // 构造源代码对象
        Map<String, StringJavaFileObject> javaFileObjects = sourceCodes.entrySet().stream()
                .map(en -> new StringJavaFileObject(en.getKey().getFullClassName(), en.getValue()))
                .collect(Collectors.toMap(StringJavaFileObject::getClassName, o -> o));
        JavaFileManager javaFileManager = new StringJavaFileManager(standardFileManager, javaFileObjects);
        // 获取一个编译任务
        JavaCompiler.CompilationTask task = getCompiler().getTask(null, javaFileManager,
                diagnosticsCollector, null, null, javaFileObjects.values());
        // TODO 后期扩展支持Processors，实现类似Lombok功能
//        task.setProcessors(processors);
        
        boolean compileComplete = task.call();
        if (compileComplete) {
            sourceCodes.keySet().forEach(result -> {
                result.setCompiledBytes(javaFileObjects.get(result.getFullClassName()).getCompiledBytes());
            });
        }
        javaFileObjects.clear();
        return compileComplete;
    }
}
